package com.demo.gateway.filter;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 认证过滤器
 * 验证用户身份和权限
 */
@Slf4j
@Component
public class AuthFilter implements GatewayFilter, Ordered {

    // 不需要认证的路径
    private static final List<String> EXCLUDE_PATHS = Arrays.asList(
            "/api/user/login",
            "/api/user/register",
            "/api/product/seckill",
            "/api/product/stock",
            "/api/product/health",
            "/api/seckill"
    );

    // 管理员接口路径
    private static final List<String> ADMIN_PATHS = Arrays.asList(
            "/api/job",
            "/api/order/cancel-expired"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        
        // 检查是否需要认证
        if (isExcludePath(path)) {
            log.debug("路径无需认证: {}", path);
            return chain.filter(exchange);
        }
        
        // 获取认证信息
        String token = getToken(request);
        if (!StringUtils.hasText(token)) {
            log.warn("缺少认证token，路径: {}", path);
            return handleUnauthorized(exchange, "缺少认证信息");
        }
        
        // 验证token
        AuthResult authResult = validateToken(token);
        if (!authResult.isValid()) {
            log.warn("token验证失败，路径: {}, 原因: {}", path, authResult.getMessage());
            return handleUnauthorized(exchange, authResult.getMessage());
        }
        
        // 检查管理员权限
        if (isAdminPath(path) && !authResult.isAdmin()) {
            log.warn("权限不足，用户: {}, 路径: {}", authResult.getUserId(), path);
            return handleForbidden(exchange, "权限不足");
        }
        
        // 在请求头中添加用户信息
        ServerHttpRequest modifiedRequest = request.mutate()
                .header("X-User-Id", String.valueOf(authResult.getUserId()))
                .header("X-User-Name", authResult.getUserName())
                .header("X-User-Role", authResult.getRole())
                .build();
        
        log.debug("认证成功，用户: {}, 路径: {}", authResult.getUserId(), path);
        
        return chain.filter(exchange.mutate().request(modifiedRequest).build());
    }

    /**
     * 检查是否为排除路径
     */
    private boolean isExcludePath(String path) {
        return EXCLUDE_PATHS.stream().anyMatch(path::startsWith);
    }

    /**
     * 检查是否为管理员路径
     */
    private boolean isAdminPath(String path) {
        return ADMIN_PATHS.stream().anyMatch(path::startsWith);
    }

    /**
     * 获取token
     */
    private String getToken(ServerHttpRequest request) {
        // 从Header中获取
        String authorization = request.getHeaders().getFirst("Authorization");
        if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) {
            return authorization.substring(7);
        }
        
        // 从参数中获取
        List<String> tokenParams = request.getQueryParams().get("token");
        if (tokenParams != null && !tokenParams.isEmpty()) {
            return tokenParams.get(0);
        }
        
        return null;
    }

    /**
     * 验证token
     * 这里简化处理，实际项目中应该调用认证服务或使用JWT
     */
    private AuthResult validateToken(String token) {
        try {
            // 模拟token验证逻辑
            if ("admin_token".equals(token)) {
                return new AuthResult(true, 1L, "admin", "ADMIN", "管理员token验证成功");
            } else if ("user_token".equals(token)) {
                return new AuthResult(true, 1001L, "user", "USER", "用户token验证成功");
            } else if (token.startsWith("valid_")) {
                // 模拟有效token
                String userIdStr = token.substring(6);
                try {
                    Long userId = Long.parseLong(userIdStr);
                    return new AuthResult(true, userId, "user" + userId, "USER", "token验证成功");
                } catch (NumberFormatException e) {
                    return new AuthResult(false, null, null, null, "token格式错误");
                }
            } else {
                return new AuthResult(false, null, null, null, "无效的token");
            }
        } catch (Exception e) {
            log.error("token验证异常", e);
            return new AuthResult(false, null, null, null, "token验证异常");
        }
    }

    /**
     * 处理未认证响应
     */
    private Mono<Void> handleUnauthorized(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("message", message);
        result.put("code", 401);
        
        String responseBody = JSON.toJSONString(result);
        DataBuffer buffer = response.bufferFactory().wrap(responseBody.getBytes(StandardCharsets.UTF_8));
        
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 处理权限不足响应
     */
    private Mono<Void> handleForbidden(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("message", message);
        result.put("code", 403);
        
        String responseBody = JSON.toJSONString(result);
        DataBuffer buffer = response.bufferFactory().wrap(responseBody.getBytes(StandardCharsets.UTF_8));
        
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -50; // 在限流过滤器之后执行
    }

    /**
     * 认证结果类
     */
    private static class AuthResult {
        private final boolean valid;
        private final Long userId;
        private final String userName;
        private final String role;
        private final String message;

        public AuthResult(boolean valid, Long userId, String userName, String role, String message) {
            this.valid = valid;
            this.userId = userId;
            this.userName = userName;
            this.role = role;
            this.message = message;
        }

        public boolean isValid() {
            return valid;
        }

        public Long getUserId() {
            return userId;
        }

        public String getUserName() {
            return userName;
        }

        public String getRole() {
            return role;
        }

        public String getMessage() {
            return message;
        }

        public boolean isAdmin() {
            return "ADMIN".equals(role);
        }
    }
}